package xyz.riceball.security.config;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import xyz.riceball.security.authentication.*;
import xyz.riceball.security.authorization.RiceBallObjectPostProcessor;
import xyz.riceball.security.common.SecurityConstant;
import xyz.riceball.usercore.entity.dto.LoginDTO;

import java.util.List;
import java.util.Map;

/**
 * <p>
 * RiceBallHttpConfigurer 增加配置类
 * </p>
 *
 * @author xiaovcloud
 * @since 2022/4/27 19:36
 */
@Getter
@Setter
@Slf4j
public class RiceBallHttpConfigurer extends AbstractHttpConfigurer<RiceBallHttpConfigurer, HttpSecurity> {

    @Override
    public void init(HttpSecurity http) throws Exception {
        //SpringSecurity的配置
        http.authorizeRequests()
                .anyRequest().authenticated()
                .withObjectPostProcessor(new RiceBallObjectPostProcessor())
                .and().logout().logoutUrl(SecurityConstant.LOGOUT_URL)
                .logoutSuccessHandler(new RiceBallAuthenticationHandler())
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().cors().configurationSource(corsConfigurationSource())
                .and().csrf().disable()
                .exceptionHandling().authenticationEntryPoint(new RiceBallAuthExceptionHandler())
                .accessDeniedHandler(new RiceBallAuthExceptionHandler());
    }

    CorsConfigurationSource corsConfigurationSource() {
        // 提供CorsConfiguration实例，并配置跨域信息
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(List.of("*"));
        corsConfiguration.setAllowedMethods(List.of("*"));
        corsConfiguration.setAllowedOrigins(List.of("*"));
        corsConfiguration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }


    @Override
    public void configure(HttpSecurity http) throws Exception {
        //获取HttpMessageConverter
        HttpMessageConverter<LoginDTO> httpMessageConverter = getHttpMessageConverter(http, true, LoginDTO.class);
        RiceBallAuthLoginFilter loginFilter =
                new RiceBallAuthLoginFilter(httpMessageConverter);
        RiceBallAuthenticationHandler generalAuthenticationHandler = new RiceBallAuthenticationHandler();
        RiceBallAuthenticationProvider authenticationProvider = new RiceBallAuthenticationProvider();
        ProviderManager providerManager = new ProviderManager(authenticationProvider);
        loginFilter.setAuthenticationManager(providerManager);
        loginFilter.setAuthenticationSuccessHandler(generalAuthenticationHandler);
        loginFilter.setAuthenticationFailureHandler(generalAuthenticationHandler);
        http.addFilterBefore(loginFilter, UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * getHttpMessageConverter
     *
     * @param http
     * @param isRead
     * @param clazz
     * @return
     */
    private HttpMessageConverter<LoginDTO> getHttpMessageConverter(HttpSecurity http, boolean isRead, Class<?> clazz) {
        ApplicationContext applicationContext = http.getSharedObject(ApplicationContext.class);
        Map<String, HttpMessageConverter> httpMessageConverterMap = applicationContext.getBeansOfType(HttpMessageConverter.class);
        HttpMessageConverter<LoginDTO> messageConverter = null;
        for (HttpMessageConverter<LoginDTO> httpMessageConverter : httpMessageConverterMap.values()) {
            if (isRead) {
                if (httpMessageConverter.canRead(clazz, MediaType.APPLICATION_JSON)) {
                    messageConverter = httpMessageConverter;
                    break;
                }
            } else {
                if (httpMessageConverter.canWrite(clazz, MediaType.APPLICATION_JSON)) {
                    messageConverter = httpMessageConverter;
                    break;
                }
            }
        }
        if (messageConverter == null) {
            throw new AuthenticationServiceException("找不到JSON解析器");
        }
        return messageConverter;
    }
}
